package com.wjhs.redispractice.accesslimit.aspect;

import com.wjhs.redispractice.accesslimit.annotation.AccessLimit;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.concurrent.TimeUnit;

/**
 * 通过AOP实现接口限流
 */
@Aspect
public class AccessLimitAspect {

    @Resource
    private RedisTemplate redisTemplate;

    @Around("@annotation(accessLimit)")
    public Object around(ProceedingJoinPoint point, AccessLimit accessLimit) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        if (attributes == null) return null;

        MethodSignature signature = (MethodSignature) point.getSignature();
        String methodName = signature.getMethod().getName();
        String className = point.getTarget().getClass().getName();

        // 构建Redis中的key，可以考虑加入类名和方法名以区分不同接口的限制
        String key = className + ":" + methodName;

        // 获取并检查限制条件
        int seconds = accessLimit.seconds();
        int maxCount = accessLimit.maxCount();
        Boolean hasKey = redisTemplate.hasKey(key);

        if (!hasKey || redisTemplate.getExpire(key, TimeUnit.SECONDS) <= 0) {
            redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        }

        long count = redisTemplate.opsForValue().increment(key);
        if (count > maxCount) {
            response.setCharacterEncoding("GBK");
            // 设置HTTP状态码
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            // 返回提示信息
            response.getWriter().write(accessLimit.msg());
            // 阻止执行
            return null;
        }
        // 继续执行目标方法
        return point.proceed();
    }

}
